package com.hxs.hxsserver.socketServer;
import com.hxs.hxsserver.config.redis.RedisService;
import com.hxs.hxsserver.config.redis.UserKey;
import com.hxs.hxsserver.domain.dto.ResponseCode;
import com.hxs.hxsserver.domain.entity.device.Device;
import com.hxs.hxsserver.exception.ApiException;
import com.hxs.hxsserver.service.SSEService;
import com.hxs.hxsserver.utils.ByteUtil;
import com.hxs.hxsserver.utils.CRCUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
@Component
public class SocketServer {
    private static final int PORT = 8083; // 监听的端口
    private ServerSocket serverSocket;
    private ExecutorService executorService;
    // 存储当前连接的客户端
    private Map<String, ClientHandler> clientHandlers = new ConcurrentHashMap<>();

    @Autowired
    private SSEService sseService;

    @Autowired
    private RedisService redisService;

    @PostConstruct
    public void start() {
        executorService = Executors.newCachedThreadPool();// 创建线程池
        try {
            serverSocket = new ServerSocket(PORT);
            log.info("Socket服务器启动，监听端口 " + PORT);

            // 处理客户端连接
            new Thread(() -> {
                try {
                    while (true) {
                        Socket clientSocket = serverSocket.accept(); // 等待客户端连接
                        log.info("客户端连接: " + clientSocket.getInetAddress());
                        ClientHandler clientHandler = new ClientHandler(clientSocket, sseService, clientHandlers, redisService);
                        executorService.submit(clientHandler); // 使用线程池处理客户端
                    }
                } catch (IOException e) {
                    log.error("服务器错误: " + e.getMessage());
                }
            }).start();
        } catch (IOException e) {
            log.error("服务器启动失败: " + e.getMessage());
        }
    }

    @PreDestroy
    public void stop() {
        try {
            if (serverSocket != null && !serverSocket.isClosed()) {
                serverSocket.close(); // 关闭服务器
                executorService.shutdown(); // 关闭线程池
                clientHandlers.clear();
                log.info("Socket服务器已关闭");
            }
        } catch (IOException e) {
            log.error("关闭服务器时出错: " + e.getMessage());
        }
    }

    public String sendMessageToClient(String deviceHardwareAddr, String message) {
        ClientHandler handler = clientHandlers.get(deviceHardwareAddr);
        // 发送消息
        if (handler != null) {
            handler.sendMessage(message);
            return "成功";
        } else {
            throw new ApiException(ResponseCode.DEVICE_STATUS_ERROR.getCode(), ResponseCode.DEVICE_STATUS_ERROR.getDesc());
        }
    }

    public void closehandler(String deviceHardwareAddr){
        //清楚客户端
        ClientHandler handler = clientHandlers.get(deviceHardwareAddr);
        if (handler != null) {
            clientHandlers.remove(deviceHardwareAddr);
        }
    }


    public class ClientHandler implements Runnable {
        private final Socket clientSocket;
        private final SSEService sseService;
        private final Map<String, ClientHandler> clientHandlers;
        private final ExecutorService executorService = Executors.newCachedThreadPool();
        private String deviceHardwareAddr;
        private final RedisService redisService;
        public ClientHandler(Socket socket, SSEService sseService, Map<String, ClientHandler> clientHandlers,RedisService redisService) {
            this.clientSocket = socket;
            this.sseService = sseService;
            this.clientHandlers = clientHandlers;
            this.redisService = redisService;
        }

        @Override
        public void run() {
            try (InputStream inputStream = clientSocket.getInputStream();
                 OutputStream outputStream = clientSocket.getOutputStream()) {
                byte[] buffer = new byte[1024];
                int bytesRead;

                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    // 将接收到的字节转换为十六进制字符串并打印（可选）
                    String messageHex = ByteUtil.bytesToHex(buffer, bytesRead);
                    log.info("收到消息来至客户端 (Hex)" + clientSocket.getInetAddress().getHostAddress() + ": " + messageHex);
                    deviceHardwareAddr = messageHex.substring(4, 14);
                    if(messageHex.length()==18){
                        //判断设备是否在服务器注册
                        Boolean exists = redisService.exists(UserKey.deviceAddr, deviceHardwareAddr);
                        if (!exists){
                            continue; // 如果该设备没有注册
                        }
                        //当是心跳包的时候，判断之前是否有该设备断链且没有清除该客户端的情况
                        if (clientHandlers.get(deviceHardwareAddr) != null) {
                            clientHandlers.remove(deviceHardwareAddr); // 从 clientHandlers 中移除
                        }
                        // 存储客户端
                        clientHandlers.put(deviceHardwareAddr, this);
                    }else {
                        // 检查客户端是否存在
                        if (clientHandlers.get(deviceHardwareAddr) == null) {
                            continue; // 如果客户端不存在，不回复消息
                        }
                    }

                    // 写入数据库逻辑
                    executorService.submit(() -> {
                        sseService.saveOrUpdate(new Device(clientSocket.getInetAddress().getHostAddress()), messageHex);
                    });

                    // 发送带时间戳和CRC的消息
                    long currentTimeMillis = System.currentTimeMillis();
                    String timestampHex = String.format("%016x", currentTimeMillis);
                    String massage = messageHex.substring(0, 14) + timestampHex;
                    String crc = CRCUtil.crcRtu(ByteUtil.hexStringToByteArray(massage));
                    byte[] bytes = ByteUtil.hexStringToByteArray(massage + crc);

                    outputStream.write(bytes, 0, bytes.length); // 发送接收到的字节
                    outputStream.flush(); // 确保数据被发送
                    log.info("发送消息 (Hex): " + ByteUtil.bytesToHex(bytes, bytes.length));
                }

                log.info("客户端已断开连接: " + clientSocket.getInetAddress());
//                sseService.updateStatusDevice(deviceHardwareAddr, 0);
//                clientHandlers.remove(deviceHardwareAddr); // 从 clientHandlers 中移除
            } catch (IOException e) {
                log.error("读取消息时出错: " + e.getMessage());
//                sseService.updateStatusDevice(deviceHardwareAddr, 0);
//                clientHandlers.remove(deviceHardwareAddr); // 从 clientHandlers 中移除
            } finally {
                try {
                    clientSocket.close(); // 关闭客户端连接
                    clientHandlers.remove(deviceHardwareAddr); // 从 clientHandlers 中移除
                    sseService.updateStatusDevice(deviceHardwareAddr, 0);
                } catch (IOException e) {
                    log.error("关闭连接时出错: " + e.getMessage());
                }
            }
        }

        public void sendMessage(String message) {
            try {
                OutputStream outputStream = clientSocket.getOutputStream();
                byte[] bytes = ByteUtil.hexStringToByteArray(message);
                outputStream.write(bytes);
                outputStream.flush();
                log.info("主动发送消息: " + message);
            } catch (IOException e) {
                log.error("发送消息时出错: " + e.getMessage());
            }
        }

    }
}
